htmlレンダリング: タグの描画
h関数を実装する!
1. まずは型定義!
code: packages/runtime-core/vnode.ts
export interface Vnode {
type: string;
props: VnodeProps;
children: (Vnode | string)[];
}
export interface VnodeProps {
}
2. h関数を実装
code: packages/runtime-core/h.ts
import { Vnode, VnodeProps } from "./vnode";
export function h(
type: string,
props: VnodeProps,
children: (Vnode | string)[]
) {
return { type, props, children }; // 受け取った引数をオブジェクトにして返しているだけ
}
ここまででplaygroundを動かしてみる
code: examples/playground/main.ts
import { createApp, h } from 'chibivue';
const app = createApp({
render() {
return h('div', { class: 'container' }, 'hello'); },
});
app.mount('#app');
画面は壊れるが、vnodeのオブジェクトが返ってきていることが確認できる
code: runtime-core/apiCreateApp.ts
export function createAppAPI<HostElement>(
render: RootRenderFunction<HostElement>,
): CreateAppFunction<HostElement> {
// 省略
mount(rootContainer: HostElement) {
const vnode = rootComponent.render!();
console.log('vnode: ', vnode); // ログを仕込んでみる
render(vnode, rootContainer);
},
// 省略
};
}
ログの値 ↓ (期待通り!)
code: log.json
{
"type": "div",
"props": {
"class": "container"
},
"children": [
"hello"
]
}
3. render関数を実装して、htmlをレンダリングできるようにする!
現状のrender関数はテキストの埋め込みしかできないので、増やす
型 RendererOptions に実現したいDOM操作を加える
code: packages/runtime-core/renderer.ts
export interface RendererOptions<HostNode = RendererNode> {
createElement(type: string): HostNode; // エレメントの生成
createText(text: string): HostNode; // テキストノードの生成
setElementText(node: HostNode, text: string): void;
insert(child: HostNode, parent: HostNode, anchor?: HostNode | null): void; // 親に子を差し込み
}
render関数に vnode の レンダリング処理を実装 (一旦propsは無視)
code: packages/runtime-core/renderer.ts
export function createRenderer(options: RendererOptions) {
const {
createElement: hostCreateElement,
createText: hostCreateText,
insert: hostInsert
} = options;
function renderVnode(vnode: VNode | string) {
// ただのstringの場合はテキストノード生成
if (typeof vnode === 'string') return hostCreateText(vnode);
// Vnodeの場合はエレメントの生成
const el = hostCreateElement(vnode.type);
// 子要素がある場合は、親に差し込み
for (const child of vnode.children) {
const childEl = renderVnode(child);
hostInsert(childEl, el);
}
// 最終的な親を返す
return el;
}
const render: RootRenderFunction = (vnode, container) => {
const el = renderVnode(vnode);
hostInsert(el, container);
};
return { render };
}
RendererOptions に定義したDOM操作を実装
code: packages/runtime-dom/nodeOps.ts
export const nodeOps: RendererOptions<Node> = {
createElement: tagName => {
return document.createElement(tagName);
},
createText: (text: string) => {
return document.createTextNode(text);
},
setElementText(node, text) {
node.textContent = text;
},
insert: (child, parent, anchor) => {
parent.insertBefore(child, anchor || null);
}
};
これで画面にhtmlを描画できるようになっている!